/**
 * Copyright (C) 2011 Andrew C. Love (DNC) <dnc.app.sup@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dnc.cloak.framework.util.datatransform;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.StringTokenizer;

import com.dnc.cloak.framework.util.dataconvert.DataConversionException;
import com.dnc.cloak.framework.util.dataconvert.DataConverter;

public class DataTransformer {
	
    protected static final String NULL = "null";
    protected static final String DEFAULT_TIMESTAMP_FORMAT = "yyyy-MM-dd-HH.mm.ss.SSS";
    protected static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    protected static final String EMPTY_STRING = "";
    protected static final String SPACE_STRING = " ";
    //private static final String UNKNOWN_TYPE = "Unknown type";
    
    /**
     * Converts @data to a string via the toString() method, and then Pads,either leading or trailing, with a specified character.
     * @param data The data that will be transformed
     * @param length The length the string should be padded to
     * @param padChacter The character to pad with
     * @param padLeading Pad if true else pad trailing
     */
    public static String padString( Object data, Integer length, Character padChacter, Boolean padLeading )throws TransformException {        

    	if (data == null) {
            return null;
        }
        else {
            StringBuffer paddedBuffer = new StringBuffer(length.intValue());
            paddedBuffer.append(data.toString());

            if (padLeading.booleanValue()) {
                for (int i=0; i < (length.intValue() - data.toString().length()); i++) {
                    paddedBuffer.insert(0,padChacter);
                }
            }
            else {
                for (int i=0; i < (length.intValue() - data.toString().length()); i++) {
                    paddedBuffer.append(padChacter);
                }
            }
            return paddedBuffer.toString();
        }
    	
    }
    
    /**
     *Validates that a character is an ASCII character
     *@param specialCharacters The char that will be validated
    */
    public static boolean isLetter(char character,String specialCharacters) throws TransformException {
    	
        if ( (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') ) {
            return true;
        }
        else if (specialCharacters!=null) {
            return (specialCharacters.indexOf(character)>=0);
        }
        else {
            return false;
        }
        
    }

    /**
     * Converts @data to a string via the toString() method, and then Pads,either leading or trailing, with a specified character.
     * @param data The data that will be transformed
     * @param length The length the string should be padded to
     * @param padChacter The chacter to pad with
     * @param padLeading Pad if true else pad trailing
     */
    public static String padString( Object data, int length, char padChacter, boolean padLeading ) throws TransformException {
    	
        if (data == null) {
            return null;
        }
        else {
            StringBuffer paddedBuffer = new StringBuffer(length);
            paddedBuffer.append(data.toString());

            if (padLeading) {
                for (int i=0; i < (length - data.toString().length()); i++) {
                    paddedBuffer.insert(0,padChacter);
                }
            }
            else {
                for (int i=0; i < (length - data.toString().length()); i++) {
                    paddedBuffer.append(padChacter);
                }
            }
            
            return paddedBuffer.toString();
            
        }
        
    }

    /**
     * Converts @data to a string via the toString() method, and then replaces characters.
     * @param data The object to be converted
     * @param findSymbol The string to find
     * @param replaceSymbol The string to replace with
     */
    public static String replaceSymbols( Object data, String findSymbol, String replaceSymbol ) throws TransformException {
    	
        if (data == null) {
            return null;
        }
        else {
            StringBuffer newString = new StringBuffer(data.toString());
            int findIndex = newString.indexOf(findSymbol);

            while (findIndex > -1) {
                newString.replace(findIndex,findIndex + findSymbol.length(),replaceSymbol);
                findIndex = newString.indexOf(findSymbol,findIndex + replaceSymbol.length());
            }
            
            return newString.toString();
            
        }
        
    }

    /**
     * Converts @data to a string via the toString() method, and then replaces characters.
     * @param data The object to be converted
     * @param findSymbols The array of strings to find
     * @param replaceSymbol The string to replace with
     */
    public static String replaceSymbols( Object data, String[] findSymbols, String replaceSymbol ) throws TransformException {
    	
        if (data == null) {
            return null;
        }
        else {
            String newString = data.toString();

            for (int i=0; i < findSymbols.length; i++ ) {
                newString = replaceSymbols(newString,findSymbols[i],replaceSymbol);
            }
            
            return newString;
        }
    }

    /**
     * Converts @data to a delimited string
     * @param data The object to be converted
     */
    public static String toDelimitedString( Object[] data, String delimiter) throws TransformException {
    	
        String delimitedString = "";
        
        try {
			if (data instanceof Object[]){
			    for (int i = 0; i < data.length; i++) {
			        delimitedString += DataConverter.getString(data[i]);
			        if (i < (data.length-1)) {
			            delimitedString+=delimiter;
			        }
			    }
			}
			else {
			    delimitedString = DataConverter.getString(data);
			}
		} catch (DataConversionException ex) {
			throw new TransformException(ex);
		}

        return delimitedString;
        
    }

    /**
     * Converts @data to a string via the toString() method, and then the string is converted to a String object.
     * @param data The object to be converted
     */
    public static String[] toStringArray( Object[] data ) throws TransformException {
    	
        try {
			if (data instanceof Object[]){
			    String[] array = new String[data.length];
			    for (int i = 0; i < data.length; i++) {
			        array[i] = DataConverter.getString(data[i]);
			    }
			    return array;
			}
			else {
			    return new String[] {DataConverter.getString(data)};
			}
		} 
        catch (DataConversionException ex) {
			throw new TransformException(ex);
		}
        
    }

    /**
     * Converts @data to a string via the toString() method, and then the string is converted to a String object.
     * @param data The object to be converted
     */
    public static String[] toStringArray(Object data, String delimiter) throws TransformException{
    	
        try {
			if (data instanceof Object[]){
			    Object[] objectArray = (Object[])data;
			    String[] array = new String[objectArray.length];
			    for (int i = 0; i < objectArray.length; i++) {
			        array[i] = DataConverter.getString(objectArray[i]);
			    }
			    return array;
			}
			else if (data instanceof String){
			    String temp = data.toString();
			    StringTokenizer tokenizer = new StringTokenizer(temp,delimiter);
			    int size = tokenizer.countTokens();
			    String[] array = new String[size];
			    int i=0;
			    while (tokenizer.hasMoreTokens()){
			        array[i] = tokenizer.nextToken();
			        i++;
			    }
			    return array;
			}
			else {
			    return new String[] {DataConverter.getString(data)};
			}
		} 
        catch (DataConversionException ex) {
			throw new TransformException(ex);
		}
        
    }


    /**
     * Converts @data to a string via the toString() method, and then the string is trimmed and the developer can specify to trim leading, trailing or both.
     * @param trimLeading  Trim leading spaces
     * @param trimTrailing Trim trailing spaces
     */
    public static String trimString( Object data, boolean trimLeading, boolean trimTrailing ) throws TransformException {
        
    	if (data == null) {
            return null;
        }
        else {
            if (trimLeading && trimTrailing) {
                return data.toString().trim();
            }
            else if (trimLeading) {
                String s = data.toString();
                int i = 0;

                while (i < s.length() && s.charAt(i) == ' ') {
                    i++;
                }
                
                return s.substring(i);
            }
            else if (trimTrailing) {
                String s = data.toString();

                int i = (s.length() - 1);

                while (i >= 0 && s.charAt(i) == ' ') {
                    i--;
                }

                return s.substring(0,i+1);
            }
            
            return data.toString();
            
        }
    	
    }

    /**
     *Removes NonAlpha characters from a string
     *@param data The data that will be used
     *@param specialChars SpecialCharacters to not remove
    */
    public static String removeNonAlphaCharacters(Object data,String specialChars) throws TransformException {
    	
        if (data!=null && data.toString().length()>0) {
            String dataString = data.toString();
            int length = dataString.length();
            StringBuffer buffer = new StringBuffer(length);

            for (int i = 0; i<length; i++) {
                if (isLetter(dataString.charAt(i),specialChars)) {
                    buffer.append(dataString.charAt(i));
                }
            }
            
            return buffer.toString();
            
        }
        else {
            return null;
        }
        
    }

    /**
     *Remove characters from a string
     *@param data The data that will be used
     *@param removeChars Characters to remove
    */
    public static String removeCharacters(Object data,String removeChars) throws TransformException {
    	
        if (data!=null && data.toString().length()>0){
            String dataString = data.toString();
            int length = dataString.length();
            StringBuffer buffer = new StringBuffer(length);

            for (int i = 0; i<length; i++) {
                if (removeChars.indexOf(dataString.charAt(i))<0) {
                    buffer.append(dataString.charAt(i));
                }
            }
            
            return buffer.toString();
            
        }
        else {
            return null;
        }
        
    } 
    
    /**
     * toMaskedNumeric is meant to be used for transforming numeric data to complex formatted string data.
     * @param data The data that will be transformed
     * @param numericMask
     * To specify a negative and a positive mask, place a ';' in the mask.  Characters following the ';'
     * are the negative mask.
     * are
     * Supported picture masks are:
     * <ul>
     * <li>$=Currency Symbol
     * <li>+=Signed data
     * <li>-=Signed data
     * <li>0=Zero fill number
     * <li>#=Optional number
     * <li>.=decimal point
     * <li>,=grouping separator
     * <li>Any other character will appear literally in output.
     * </ul>
     * Examples:
     * <ul>
     * <li><b>MASK=$000,000.00;(-$###,000.00)</b>
     * <li>999.99 transformed to $999.99
     * <li>9999.99 transformed to $9,999.99
     * <li>9.99 transformed to $009.99
     * <li>-9.99 transformed to (-$009.99)
     */
    public static String toMaskedNumeric( Object data, String numericMask ) throws TransformException {
        if (data == null) {
            return EMPTY_STRING;
        }
        else {
            StringBuffer mask = new StringBuffer(numericMask);

            String transformedData = EMPTY_STRING;
            try {
                Double doubleValue = new Double(data.toString());
                DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
                df.applyPattern(mask.toString());
                transformedData = df.format(doubleValue.doubleValue());
            }
            catch (Exception ex) {
                throw new TransformException(ex);
            }
            
            return transformedData;
        }
        
    }
    
}
